package auction.chapter16.dao.hibernate;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Example;

import auction.chapter16.dao.GenericDAO;

import persistence.HibernateUtil;

public abstract class GenericHibernateDAO<T,ID extends Serializable> implements GenericDAO<T, ID> {
	private Class<T> persistentClass;
	private Session session;
	
	public GenericHibernateDAO(){
		Type t=this.getClass().getGenericSuperclass();
		//sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
		System.out.println("!!!!!GenericSuperclass "+t.getClass().getName());
		
		//auction.chapter16.dao.hibernate.GenericHibernateDAO<auction.model.Bid, java.lang.Long>
		System.out.println("!!!!!t "+t);
		ParameterizedType pt=(ParameterizedType)t;
		
		Type[] types=pt.getActualTypeArguments();
		for(Type tt : types){
			//java.lang.Class<->class auction.model.Bid
			//java.lang.Class<->class java.lang.Long
			System.out.println("   !"+tt.getClass().getName()+"<->"+tt);
		}
		
		this.persistentClass=(Class<T>)types[0];
	}
	
	public void setSession(Session s){
		this.session=s;
	}
	
	public Session getSession(){
		if(session == null){
			session=HibernateUtil.getSessionFactory().getCurrentSession();
		}
		
		return session;
	}
	
	public Class<T> getPersistentClass(){
		return persistentClass;
	}
	
	public T findById(ID id,boolean lock){
		T entity;
		if(lock){
			entity = (T) getSession().load(getPersistentClass(), id,LockMode.UPGRADE);
		}else{
			entity = (T) getSession().load(getPersistentClass(), id);
		}
		
		return entity;
		
	}
	
	public List<T> findAll(){
		return findByCriteria();
	}
	
	public List<T> findByExample(T ex,String... excludedProperty){
		Criteria crit=getSession().createCriteria(getPersistentClass());
		
		Example example=Example.create(ex);
		for(String exclude : excludedProperty){
			example.excludeProperty(exclude);
		}
		
		crit.add(example);
		return crit.list();
	}
	
	public T makePersistent(T entity){
		getSession().saveOrUpdate(entity);
		return entity;
	}
	
	public void makeTransient(T entity){
		getSession().delete(entity);
	}
	
	public void flush(){
		getSession().flush();
	}
	
	public void clear(){
		getSession().clear();
	}
	
	/**
	 * Use this inside subclasses as a convenience method
	 * 
	 * the subclass may has getMaxBid() then use this method in superlcass GOOD!!!
	 * 
	 * @param criterion
	 * @return
	 */
	protected List<T> findByCriteria(Criterion... criterion){
		Criteria crit=getSession().createCriteria(getPersistentClass());
		for(Criterion c : criterion){
			crit.add(c);
		}
		
		return crit.list();
	}
	
}
